/* CombinationLauncher.cs
 * Author: Richard Broker
 * 
 *  This file is part of Tame.
 *
 *  Tame is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  Tame is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Tame. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Threading;
// System.Numeric;
using System.IO;

namespace Tame
{
    public partial class CombinationLauncher : Form
    {
        int MAXIMUM_UNREPEATABLE_COMBINATIONS = 749398; //33390720; // Possible 749398 as order is unimportant, repetition allowed (so we can use a gun more than once)
        int[] BestGun = { 0, 0, 0, 0, 0, 0, 0 };
        int sizeLimit = 0;
        int gun_Mounts = 0;
        int armor_type = 0;
        double armor_strength = 1;
        int OWEAPSPEC = -1;
        int EMWEAPSPEC = -1;
        int CONVWEAPSPEC = -1;


        // 1. Set up bools:
        bool excludeOrganicWeapons = false;
        bool excludeEMWeapons = false;
        bool excludeConvWeapons = false;
        bool excludePardusWeapons = false;
        bool excludeEmpGear = false;
        public int[] ExactAlgoResult;
        public int[] parentLoopIndexes = {0,0,0,0,0,0,0};
        static int totalOutcomesEnumerated = 0;
        static int prevHighestDamage = 0;
        static int weightOfCurrentHighest;

        ComboBox armorCombo = null;
        Guns Gun = new Guns();

        public CombinationLauncher(int SIZE_LIMIT, int GUN_MOUNTS, double ARMOR_STRENGTH, int ARMOR_TYPE, bool excludeO, bool excludeEM, bool excludeConv, bool excludeEx, bool excludeEmp, int oWeapSpec, int emWeapSpec, int convWeapSpec)
        {
            sizeLimit = SIZE_LIMIT;
            gun_Mounts = GUN_MOUNTS;
            armor_type = ARMOR_TYPE;
            armor_strength = ARMOR_STRENGTH;

            excludeOrganicWeapons = excludeO;
            excludeEMWeapons = excludeEM;
            excludeConvWeapons = excludeConv;
            excludePardusWeapons = excludeEx;
            excludeEmpGear = excludeEmp;

            OWEAPSPEC = oWeapSpec;
            EMWEAPSPEC = emWeapSpec;
            CONVWEAPSPEC = convWeapSpec;
            totalOutcomesEnumerated = 0;
            prevHighestDamage = 0;
             weightOfCurrentHighest = sizeLimit;

            InitializeComponent();
        }

        private void checkButton_Click(object sender, EventArgs e)
        {
            checkForLabel.Text = "Determining Combinations:";
            huntButton.Visible = false;
            dontCheckButton.Visible = false;
            loadingPictureBox.Visible = true;

            // Set up progress bar
            permutationProgress.Maximum = MAXIMUM_UNREPEATABLE_COMBINATIONS;
            permutationProgress.Visible = true;
            huntProgresslabel.Text = "0/" + MAXIMUM_UNREPEATABLE_COMBINATIONS.ToString();

            // run background worker to keep GUI responsive.
            //MessageBox.Show(string.Format("Conv: {0}, EM: {1}, Org: {2}, Pardus: {3}, Emp: {4}", excludeConvWeapons, excludeEMWeapons, excludeOrganicWeapons, excludePardusWeapons, excludeEmpGear)); 
            bgWorker.RunWorkerAsync();
            //checkingTest();
            huntButton.Enabled = false;
        }

        private void checkingTest()
        {
            int tempGun = 0;
            for (tempGun = 0; tempGun < 37; tempGun++)
            {
                if (((excludeEmpGear == true) && ((tempGun > 11) && (tempGun < 25))) || ((excludeOrganicWeapons == true) && ((tempGun >= 25) && (tempGun < 29))) || ((excludeEMWeapons == true) && ((tempGun >= 29) && (tempGun < 33))) || ((excludePardusWeapons == true) && ((tempGun >= 33))) || ((excludeConvWeapons == true) && ((tempGun >= 0) && (tempGun < 25))))
                {
                    MessageBox.Show(string.Format("We are excluding: {0}", Gun.determineGunName(tempGun)));
                }
            }
        }

        private void dontCheckButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Start off the recursive search with the first gun indexed from 0.
            enumerateCombinations(37, 1, gun_Mounts, 0);
            
            bgWorker.CancelAsync();
        }

        // http://www.codingthewheel.com/archives/exhaustively-enumerating-combinations-and-permutations-in-code#combinations_without_repetition
        // This function needs to dynamically generate nested "for" loops, with
        // the number of nested loops being variable (1-5).
        // Recursion seems a good bet for this, so lets give it a shot:
        public int enumerateCombinations(int possibleGuns, int currentGun, int gunMounts, int parentIndex)
        {            
            int modeDamage = 0;
            int modeFireRate = 1;
            int modeWeight = 2;            
            int[] tempGun = { 0, 0, 0, 0, 0, 0, 0 };
            string[] gunNames = { "", "", "", "", "" };
            double[] GunDamages = { 0, 0, 0, 0, 0 };
            int[] GunFireRates = { 0, 0, 0, 0, 0 };
            int[] GunWeights = { 0, 0, 0, 0, 0 };
            int[] GunTypes = { 0, 0, 0, 0, 0 };
            int[] lastSet = { 0, 0, 0, 0, 0 };
            bool typeConflict = false;
            bool ignoreThisSet = false;
            double[] Efficiency = { 0, 0, 0, 0, 0 };


           if((currentGun < gunMounts)) // More loops
            {
                for (int i = parentIndex; i < possibleGuns; i++)
                {
                    parentLoopIndexes[currentGun-1] = i;
                    totalOutcomesEnumerated = enumerateCombinations(possibleGuns, currentGun +1, gunMounts, i);
                }
            }
            if ((currentGun == gunMounts)) // This is the innermost loop!
            {                
                for (int i = parentIndex; i < possibleGuns; i++)
                {
                    parentLoopIndexes[5] = 0;
                    parentLoopIndexes[6] = 0;
                    parentLoopIndexes[gunMounts-1] = i;

                    for (int k = 0; k < gunMounts; k++)
                    {
                        if (parentLoopIndexes[k] != lastSet[k]) // Don't gather data we don't need.
                        {
                            gunNames[k] = Gun.determineGunName(parentLoopIndexes[k]);
                            GunFireRates[k] = Gun.GetGunData(ref modeFireRate, gunNames[k]);
                            GunWeights[k] = Gun.GetGunData(ref modeWeight, gunNames[k]);
                            GunTypes[k] = Gun.determineGunType(parentLoopIndexes[k]);
                            typeConflict = Gun.DetectTypeConflicts(GunTypes, k);
                            Efficiency[k] = Gun.determineGunEfficiency(GunTypes[k], true, armor_type, armorCombo);

                            // Damage including firerates
                            GunDamages[k] = ((Gun.GetGunData(ref modeDamage, gunNames[k])) * GunFireRates[k]);
                            GunDamages[k] = Gun.determineGunDamageWithWeapSpec(GunDamages[k], GunTypes[k], CONVWEAPSPEC, OWEAPSPEC, EMWEAPSPEC);
                            GunDamages[k] = (((GunDamages[k] / 100) * Efficiency[k]) / armor_strength);
                        }
                    }

                    
                    for (int j = 0; j < gunMounts; j++)
                    {
                        parentLoopIndexes[5] += (int)GunDamages[j];
                        parentLoopIndexes[6] += GunWeights[j];
                        lastSet[j] = parentLoopIndexes[j]; // Last last guns, we don't need to gather their data again if it's the same gun from last time.                                                    
                    }
                    ignoreThisSet = false;

                    if ((((parentLoopIndexes[5] > prevHighestDamage) && (parentLoopIndexes[6] <= sizeLimit))) || ((parentLoopIndexes[5] == prevHighestDamage) && (parentLoopIndexes[6] <= sizeLimit) && (parentLoopIndexes[6] < weightOfCurrentHighest)) && (typeConflict == false))
                    {
                        for (int j = 0; j < gunMounts; j++)
                        {
                            if (((excludeEmpGear == true) && ((parentLoopIndexes[j] > 11) && (parentLoopIndexes[j] < 25))) || ((excludeOrganicWeapons == true) && ((parentLoopIndexes[j] >= 25) && (parentLoopIndexes[j] < 29))) || ((excludeEMWeapons == true) && ((parentLoopIndexes[j] >= 29) && (parentLoopIndexes[j] < 33))) || ((excludePardusWeapons == true) && ((parentLoopIndexes[j] >= 33))) || ((excludeConvWeapons == true) && ((parentLoopIndexes[j] >= 0) && (parentLoopIndexes[j] < 25))) || (typeConflict == true))
                            {
                                ignoreThisSet = true;
                            }
                        }
                        if (ignoreThisSet == false)
                        {                            
                            weightOfCurrentHighest = parentLoopIndexes[6];
                            prevHighestDamage = parentLoopIndexes[5];
                            BestGun[5] = parentLoopIndexes[5];
                            BestGun[6] = parentLoopIndexes[6];

                            for (int j = 0; j < gun_Mounts; j++)
                            {
                                BestGun[j] = parentLoopIndexes[j];                                                         
                            }
                        }
                    }

                    // Each iteration of this loop visits a single outcome  
                    totalOutcomesEnumerated++;


                    if ((i == 0) || (i == 15) || (i == 37))
                    {
                        bgWorker.ReportProgress(0, totalOutcomesEnumerated);
                    }
              
                }
            }
            //bgWorker.ReportProgress(0, totalOutcomesEnumerated);
                return totalOutcomesEnumerated;
        }            

        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            GetReturnValue();
            this.Close();
        }

        public void GetReturnValue()
        {
            int[] gunNames = { 0, 0, 0, 0, 0, 0, 0 };
            int i;

            for (i = 0; i < BestGun.Length; i++)
            {
                try
                {
                    gunNames[i] = BestGun[i];
                }
                catch (Exception)
                {
                }
            }

            ExactAlgoResult = gunNames;
            this.Close();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            try
            {
                permutationProgress.Value = int.Parse(e.UserState.ToString());
                huntProgresslabel.Text = e.UserState.ToString() + "/" + MAXIMUM_UNREPEATABLE_COMBINATIONS.ToString();
            }
            catch (Exception)
            {
            }

        }

        private void PermutationLauncher_FormClosed(object sender, FormClosedEventArgs e)
        {
            bgWorker.CancelAsync();
        }

        private void CombinationLauncher_Load(object sender, EventArgs e)
        {
            if (gun_Mounts == 4)
            {
                MAXIMUM_UNREPEATABLE_COMBINATIONS = 91390;
            }
            if (gun_Mounts == 3)
            {
                MAXIMUM_UNREPEATABLE_COMBINATIONS = 9139;
            }
            if (gun_Mounts == 2)
            {
                MAXIMUM_UNREPEATABLE_COMBINATIONS = 703;
            }
            if (gun_Mounts == 1)
            {
                MAXIMUM_UNREPEATABLE_COMBINATIONS = 37;
            }
        }
    }
}
